﻿using System;
using System.Collections.Generic;
using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using Mekiasm.Ore;
using Mekiasm.Plants;
using TemplatesDatabase;

namespace Mekiasm
{
    public class SubsystemMekBlockBehavior : SubsystemBlockBehavior, IUpdateable, IDrawable
    {
        public override int[] HandledBlocks => new int[] {
            MekPlantsBlock.Index,
            MekOreBlock.Index,
            MekChestBlock.Index,
            MekElectricBlock.Index,
            MekToolBlock.Index,
            MekOreChunkBlock.Index,
            MekFlatBlock.Index,
            MekAlphaBlock.Index,
            MekSaveItemBlock.Index,
            MekFluidBlock.Index
        };
        public UpdateOrder UpdateOrder => UpdateOrder.SubsystemPlayers;
        public static List<Func<bool>> CacheTasks = new List<Func<bool>>();
        public static ValuesDictionary SaveData;
        public static List<Common.DrawItem> DrawItems = new List<Common.DrawItem>();
        public int[] DrawOrders => new int[] { 1 };
        public static MekModelGenerator mekModelGenerator;
        public static SubsystemThrowSystem SubsystemThrowSystem;
        public override bool OnUse(Ray3 ray, ComponentMiner componentMiner)
        {
            TerrainRaycastResult? raycastResult = componentMiner.Raycast<TerrainRaycastResult>(ray, RaycastMode.Digging, true, true, false);
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(componentMiner.ActiveBlockValue)] as MekBlock;
            return mek.OnUse(ray, componentMiner, raycastResult);
        }
        public override bool OnHitAsProjectile(CellFace? cellFace, ComponentBody componentBody, WorldItem worldItem)
        {
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(worldItem.Value)] as MekBlock;
            return mek.OnHitAsProjectile(cellFace, componentBody, worldItem);
        }
        public override void OnCollide(CellFace cellFace, float velocity, ComponentBody componentBody)
        {
            int v = ILibrary.SubsystemTerrain.Terrain.GetCellValueFast(cellFace.X, cellFace.Y, cellFace.Z);
            int id = Terrain.ExtractContents(v);
            MekBlock mek = BlocksManager.Blocks[id] as MekBlock;
            if (mek != null && mek.Collidable(v)) mek.OnCollide(v, cellFace, velocity, componentBody);
        }
        public override void OnBlockModified(int value, int oldValue, int x, int y, int z)
        {
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            mek.OnBlockModified(value, oldValue, x, y, z);
        }
        public override void OnBlockAdded(int value, int oldValue, int x, int y, int z)
        {
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            mek.OnBlockAdded(this, value, oldValue, x, y, z);
        }
        public override void OnBlockGenerated(int value, int x, int y, int z, bool isLoaded)
        {
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            mek.OnBlockGenerated(value, x, y, z, isLoaded);
        }
        public override void OnBlockRemoved(int value, int newValue, int x, int y, int z)
        {
            TerrainChunk terrainChunk = ILibrary.SubsystemTerrain.Terrain.GetChunkAtCell(x, z);
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            mek.OnBlockRemoved(this, value, newValue, x, y, z);
            for (int i = 0; i < DrawItems.Count; i++)
            {
                Common.DrawItem drawItem = DrawItems[i];
                if (drawItem.TerrainChunk.Coords == terrainChunk.Coords)
                {
                    DrawItems.RemoveAt(i);
                    break;
                }
            }
            if (ILibrary.TerrainGeometrys.ContainsKey(new Point3(x, y, z))) ILibrary.TerrainGeometrys.Remove(new Point3(x, y, z));
        }
        public override void OnChunkInitialized(TerrainChunk chunk)
        {
            PlantSystem.OnChunkInitialized(chunk);
            generateMiner(chunk);
            generatePlant(chunk, TallGrassBlock.Index, 60, 70, 0.05f);
        }
        public override void OnChunkDiscarding(TerrainChunk chunk)
        {
            PlantSystem.OnChunkDiscarding(chunk);
            AnimateTextureSystem.OnChunkDiscarding(chunk);
            SubsystemSingleChestDraw.OnChunkDiscarding(chunk);
            List<Common.DrawItem> toRemove = new List<Common.DrawItem>();
            for (int i = 0; i < DrawItems.Count; i++)
            {
                Common.DrawItem drawItem = DrawItems[i];
                if (drawItem.TerrainChunk.Coords == chunk.Coords)
                {
                    toRemove.Add(drawItem);
                }
            }
            for (int i = 0; i < toRemove.Count; i++)
            {
                DrawItems.Remove(toRemove[i]);
            }


        }
        public override void OnHitByProjectile(CellFace cellFace, WorldItem worldItem)
        {
            int value = ILibrary.SubsystemTerrain.Terrain.GetCellValueFast(cellFace.X, cellFace.Y, cellFace.Z);
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            mek.OnHitByProjectile(cellFace, worldItem, value);
        }
        public override bool OnEditBlock(int x, int y, int z, int value, ComponentPlayer componentPlayer)
        {
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            return mek.OnEditBlock(x, y, z, value, componentPlayer);
        }
        public override bool OnEditInventoryItem(IInventory inventory, int slotIndex, ComponentPlayer componentPlayer)
        {
            int value = inventory.GetSlotValue(slotIndex);
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            return mek.OnEditInventoryItem(value, inventory, slotIndex, componentPlayer);
        }
        public override bool OnInteract(TerrainRaycastResult raycastResult, ComponentMiner componentMiner)
        {
            int Index = ILibrary.SubsystemTerrain.Terrain.GetCellContents(raycastResult.CellFace.X, raycastResult.CellFace.Y, raycastResult.CellFace.Z);
            MekBlock mek = BlocksManager.Blocks[Index] as MekBlock;
            return mek.OnInteract(this, raycastResult.Value, raycastResult, componentMiner);
        }
        public override void OnNeighborBlockChanged(int x, int y, int z, int neighborX, int neighborY, int neighborZ)
        {//附近方块改变
            int value = ILibrary.SubsystemTerrain.Terrain.GetCellValueFast(x, y, z);
            MekBlock mek = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            mek.OnNeighborBlockChanged(value, x, y, z, neighborX, neighborY, neighborZ);
        }
        public override bool OnAim(Ray3 aim, ComponentMiner componentMiner, AimState state)
        {
            int value = componentMiner.ActiveBlockValue;
            MekBlock mekBlock = BlocksManager.Blocks[Terrain.ExtractContents(value)] as MekBlock;
            if (mekBlock != null) return mekBlock.OnAim(value, aim, componentMiner, state);
            return base.OnAim(aim, componentMiner, state);
        }
        public override void Load(ValuesDictionary valuesDictionary)
        {
            ILibrary.Project = Project;
            ILibrary.Players = Project.FindSubsystem<SubsystemPlayers>(true);
            ILibrary.SubsystemTerrain = Project.FindSubsystem<SubsystemTerrain>(true);
            ILibrary.SubsystemBlockEntities = Project.FindSubsystem<SubsystemBlockEntities>(true);
            ILibrary.SubsystemSky = Project.FindSubsystem<SubsystemSky>(true);
            ILibrary.SubsystemAudio = Project.FindSubsystem<SubsystemAudio>(true);
            ILibrary.SubsystemGameInfo = Project.FindSubsystem<SubsystemGameInfo>(true);
            ILibrary.SubsystemMovingBlocks = Project.FindSubsystem<SubsystemMovingBlocks>(true);
            ILibrary.SubsystemTime = Project.FindSubsystem<SubsystemTime>(true);
            ILibrary.subsystemTimeOfDay = Project.FindSubsystem<SubsystemTimeOfDay>(true);
            ILibrary.SubsystemPickables = Project.FindSubsystem<SubsystemPickables>(true);
            ILibrary.SubsystemParticles = Project.FindSubsystem<SubsystemParticles>(true);
            ILibrary.subsystemBodies = Project.FindSubsystem<SubsystemBodies>(true);
            ILibrary.subsystemProjectiles = Project.FindSubsystem<SubsystemProjectiles>();
            ILibrary.subsystemSpawn = Project.FindSubsystem<SubsystemSpawn>();
            ILibrary.subsystemExplosions = Project.FindSubsystem<SubsystemExplosions>();
            ILibrary.subsystemParticles = Project.FindSubsystem<SubsystemParticles>();
            ILibrary.contentsGenerator = new TerrainContentsGenerator22(ILibrary.SubsystemTerrain);
            ILibrary.SubsystemPickables.PickableRemoved += ILibrary.PickablesRemove;
            ILibrary.TerrainUpdater = ILibrary.SubsystemTerrain.TerrainUpdater;
            ILibrary.subsystemAnimatedTextures = Project.FindSubsystem<SubsystemAnimatedTextures>();
            ILibrary.updateStatistics = ILibrary.TerrainUpdater.m_statistics;
            SubsystemThrowSystem = new SubsystemThrowSystem(Project);
            mekModelGenerator = new MekModelGenerator();
            SaveData = valuesDictionary.GetValue("SaveData", new ValuesDictionary());
            TransmitMachine.Load(SaveData.GetValue("TransmitMachine", new ValuesDictionary()));
            PlantSystem.Load(valuesDictionary.GetValue("PlantsData", new ValuesDictionary()));
            SaveItemSystem.Load(valuesDictionary.GetValue("SaveItem", new ValuesDictionary()));
            ValuesDictionary MakePoints = valuesDictionary.GetValue<ValuesDictionary>("MakePoints");
            LightSystem.MakeCount = MakePoints.GetValue("MakeCount", 0);
            foreach (Point3 point1 in MakePoints.GetValue("List", new ValuesDictionary()).Values)
            {
                LightSystem.MakePoints.Add(point1);
            }

        }
        public override void Save(ValuesDictionary valuesDictionary)
        {
            valuesDictionary.SetValue("SaveData", SaveData);
            SaveData.SetValue("TransmitMachine", TransmitMachine.Save());
            valuesDictionary.SetValue("PlantsData", PlantSystem.Save());
            valuesDictionary.SetValue("SaveItem", SaveItemSystem.Save());
            ValuesDictionary MakePoints = new ValuesDictionary();
            ValuesDictionary list = new ValuesDictionary();
            MakePoints.SetValue("MakeCount", LightSystem.MakeCount);
            for (int i = 0; i < LightSystem.MakePoints.Count; i++)
            {
                list.SetValue(i.ToString(), LightSystem.MakePoints[i]);
            }
            MakePoints.SetValue("List", list);
            valuesDictionary.SetValue("MakePoints", MakePoints);

        }
        public override void Dispose()
        {
            DrawItems.Clear();
            ElectricSystem.Dispose();
            FluidSystem.Dispose();
            ItemSystem.Dispose();
            GasSystem.Dispose();
            PlantSystem.Dispose();
            SaveItemSystem.Dispose();
            SubsystemSingleChestDraw.Dispose();
            AnimateBlockSystem.Dispose();
            AnimateTextureSystem.Dispose();
        }
        public void Update(float dt)
        {
            MekSoundSystem.Update();
            ElectricSystem.Update(dt);
            FluidSystem.Update(dt);
            GasSystem.Update(dt);
            ItemSystem.Update(dt);
            PlantSystem.Update(dt);
            LightSystem.Update();
            AnimateBlockSystem.Update();
            SubsystemThrowSystem.Update(dt);
            if (CacheTasks.Count > 0)
            {
                for (int i = 0; i < CacheTasks.Count; i++)
                {
                    if (CacheTasks[i]()) CacheTasks.RemoveAt(i);
                }
            }
        }
        public void Draw(Camera camera, int drawOrder)
        {
            SubsystemThrowSystem.Draw(camera);
        }

        public void generateMineralLikeCoal(TerrainChunk chunk, int value, int replacevalue, int minHeight, int maxHeight, float frequency = 0.33f)
        { //生成矿物算法-类似煤的生成概率                                             
            int cx = chunk.Coords.X;
            int cy = chunk.Coords.Y;
            List<TerrainBrush> terrainBrushes = new List<TerrainBrush>();
            for (int i = 0; i < 16; i++)
            {//煤块的生成概率
                TerrainBrush terrainBrush = new TerrainBrush();
                int num = ILibrary.random.Int(4, 12);
                for (int j = 0; j < num; j++)
                {
                    Vector3 vector = 0.5f * Vector3.Normalize(new Vector3(ILibrary.random.Float(-1f, 1f), ILibrary.random.Float(-1f, 1f), ILibrary.random.Float(-1f, 1f)));
                    int num2 = ILibrary.random.Int(3, 8);
                    Vector3 zero = Vector3.Zero;
                    for (int k = 0; k < num2; k++)
                    {
                        terrainBrush.AddBox((int)MathUtils.Floor(zero.X), (int)MathUtils.Floor(zero.Y), (int)MathUtils.Floor(zero.Z), 1, 1, 1, value);
                        zero += vector;
                    }
                }
                if (i == 0) terrainBrush.AddCell(0, 0, 0, value);
                terrainBrush.Compile();
                terrainBrushes.Add(terrainBrush);
            }
            for (int i = cx - 1; i <= cx + 1; i++)
            {
                for (int j = cy - 1; j <= cy + 1; j++)
                {
                    float num2 = ILibrary.CalculateMountainRangeFactor(i * 16, j * 16);
                    int num3 = (int)(5f + 2f * num2 * SimplexNoise.OctavedNoise(i, j, frequency, 1, 1f, 1f));
                    for (int l = 0; l < num3; l++)
                    {
                        int x2 = i * 16 + ILibrary.random.Int(0, 15);
                        int y2 = ILibrary.random.Int(minHeight, maxHeight);
                        int cz = j * 16 + ILibrary.random.Int(0, 15);
                        terrainBrushes[ILibrary.random.Int(0, terrainBrushes.Count - 1)].PaintFastSelective(chunk, x2, y2, cz, replacevalue);
                    }
                }
            }
        }
        public void generatePlant(TerrainChunk terrainChunk, int replaceValue, int minHeight, int maxHeight, float probability, int? temprature = null)
        {
            if (terrainChunk.IsLoaded)
            {
                return;
            }
            int[] plants = new int[] {
                AgaveSeed.Index , AmaranthSeed.Index , ArrowrootSeed.Index ,
                ArtichokeSeed.Index,CelerySeed.Index,LeekSeed.Index,
                LettureSeed.Index,PineappleSeed.Index,TomatoSeed.Index,
                RubberSapl.Index

            };
            if (temprature.HasValue)
            {
                int temp = terrainChunk.GetTemperatureFast(terrainChunk.Origin.X, terrainChunk.Origin.Y);
                if (temp < temprature.Value) return;
            }
            for (int i = minHeight; i < maxHeight; i++)
            {
                for (int j = terrainChunk.Origin.X; j < terrainChunk.Origin.X + 16; j++)
                {
                    for (int k = terrainChunk.Origin.Y; k < terrainChunk.Origin.Y + 16; k++)
                    {
                        int value = terrainChunk.Terrain.GetCellValueFast(j, i, k);
                        if (Terrain.ExtractContents(value) == replaceValue && ILibrary.random.Bool(probability))
                        {
                            int vv = plants[ILibrary.random.Int(0, plants.Length - 1)];
                            vv = ILibrary.setExtraData(vv, 3);
                            ILibrary.SubsystemTerrain.ChangeCell(j, i, k, vv);
                        }
                    }
                }
            }
        }
        public void generateMiner(TerrainChunk chunk)
        {
            if (chunk.IsLoaded)
            {
                return;
            }
            generateMineralLikeCoal(chunk, OreUranium.Index, 67, 25, 55, 0.21f);
            generateMineralLikeCoal(chunk, OreLead.Index, 67, 20, 55, 0.21f);
            generateMineralLikeCoal(chunk, OreTin.Index, 67, 20, 80, 0.21f);
        }

    }
}
